package server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import tool.StreamUtils;
import tool.SystemUtils;
import tool.SystemUtils.Propskey;

/**
 * 服务器连接中心
 * @author lijia
 *
 */
public class ConnectionServer implements Runnable {

	private final int PORT = SystemUtils.getIntProperty(Propskey.SERVER_PORT);
	private ServerSocketChannel serverSocketChannel = null;
	private Selector selector = null;

	//请求通道
	public static final Map<SelectionKey, BlockingQueue<GpcRequest>> requestChannel = new HashMap<>();
	public static final BlockingQueue<SelectionKey> keys = new LinkedBlockingQueue<SelectionKey>();
	//响应通道
	//public static final Map<SelectionKey,BlockingQueue<GpcResponse>> responseChannel = new HashMap<>(); 
	public static final AtomicInteger idGenerator = new AtomicInteger(0);


	public static boolean isContainQueue(SelectionKey key) {
		return requestChannel.containsKey(key);
	}

	public static boolean isContainKey(SelectionKey key) {
		return keys.contains(key);
	}

	public static void addNewSelection(SelectionKey key) {
		if(!isContainKey(key)) {
			try {
				keys.offer(key,10,TimeUnit.MILLISECONDS);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public ConnectionServer() {
		try {
			serverSocketChannel = ServerSocketChannel.open();
			serverSocketChannel.configureBlocking(false);
			serverSocketChannel.bind(new InetSocketAddress(PORT));
			// 4.创建通道选择器
			selector = Selector.open();
			/*
			 * 5.注册事件类型
			 * 
			 *  sel:通道选择器
			 *  ops:事件类型 ==>SelectionKey:包装类，包含事件类型和通道本身。四个常量类型表示四种事件类型
			 *  SelectionKey.OP_ACCEPT 获取报文      SelectionKey.OP_CONNECT 连接
			 *  SelectionKey.OP_READ 读           SelectionKey.OP_WRITE 写
			 */
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		while (!Thread.currentThread().isInterrupted()) {
			// 6.获取可用I/O通道,获得有多少可用的通道
			try {
				int num = selector.select();
				if (num > 0) { // 判断是否存在可用的通道
					// 获得所有的keys
					Set<SelectionKey> selectedKeys = selector.selectedKeys();
					// 使用iterator遍历所有的keys
					Iterator<SelectionKey> iterator = selectedKeys.iterator();
					// 迭代遍历当前I/O通道
					while (iterator.hasNext()) {
						// 获得当前key
						SelectionKey key = iterator.next();
						// 判断事件类型，做对应的处理
						if(key.isValid()) {
							if (key.isAcceptable()) {
								ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel();
								SocketChannel socketChannel = ssChannel.accept();
								System.out.println("处理请求："+ socketChannel.getRemoteAddress()+" from "+key.toString());
								// 获取客户端的数据
								// 设置非阻塞状态
								socketChannel.configureBlocking(false);
								// 注册到selector(通道选择器)
								socketChannel.register(selector, SelectionKey.OP_READ);
							} else if (key.isReadable()) {
								//调用读操作工具类
								GpcRequestProcessor.ProcessorRequest(key);										
							} 
						}
						iterator.remove();
					}
				} 
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	

	public static boolean sendMessage(SelectionKey key,GpcRequest response) {
		SocketChannel sc = (SocketChannel) key.channel();
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		buffer.put(StreamUtils.serialize(response));
		buffer.flip();
		try {
			sc.write(buffer);
		} catch (IOException e) {
			System.out.println("连接异常...正在关闭连接");
			//删除异常连接的通道
			//requestChannel.remove(key);
			return false;
		}
		buffer.clear();
		return true;
	}


	public static void main(String[] args) {
		new Thread(new ConnectionServer()).start();
	}
}
